package Java编程的逻辑.第八章.泛型;

import java.util.Arrays;
import java.util.Random;

/**
 * 8.1.2 容器类
 * @param <E>
 */
public class DynamicArray<E> {
    private static final int DEFAULT_CAPACITY = 10;
    private int size;
    private Object[] elementData;

    public DynamicArray() {
        this.elementData = new Object[DEFAULT_CAPACITY];
    }

    private void ensureCapacity(int minCapacity) {
        int oldCapacity = elementData.length;
        if(oldCapacity > minCapacity) {
            return;
        }
        int newCapacity = oldCapacity * 2;
        if(newCapacity < minCapacity) {
            newCapacity = minCapacity;
        }
        elementData = Arrays.copyOf(elementData, newCapacity);
    }
    public void add(E e) {
        ensureCapacity(size + 1);
        elementData[size++] = e;
    }
    public E get(int index) {
        return (E)elementData[index];
    }
    public int size() {
        return size;
    }
    public E set(int index, E element) {
        E oldValue = get(index);
        elementData[index] = element;
        return (E)oldValue;
    }

    /**
     * 泛型方法到底用通配符的姓氏还是加类型参数，两者到底有什么关系？
     * 1） 通配符形式都可以用类型参数的形式来替代，通配符能做的用类型参数都能做
     * 2） 通配符形式可以减少类型参数，形式上可以更简单，可读性也更好，所以能用通配符的尽量用通配符
     * 3） 如果类型参数表之间有依赖关系，或者返回值依赖类型参数，或者需要写操作，则只能用类型参数
     * 4） 通配符形式和类型参数往往配合使用，比如上面的copy方法，定义必要的类型参数，使用通配符表达依赖，并接受功能广泛的数据类型。
     */

    /**
     * 上届为其他类型参数
     * @param c
     */
    public <T extends E> void addAll(DynamicArray<T> c) {
        for(int i=0; i<c.size; i++) {
            add(c.get(i));
        }
    }

    /**
     * 更简洁的参数类型限定
     * 区别：
     *      <T extends E>用于定义类型参数，它声明了一个类型参数T，可以放在泛型类定义中类名后面，泛型方法返回值前面
     *      例如：
     *          public void addAll2(DynamicArray<? extends E> c){}
     *          public <? extends E> void addAll2(DynamicArray c){}
     *
     *      <? extends E>用于实例化类型参数，它用于实例化泛型变量中的类型参数，只是这个具体类型是未知的，只知道他是E或E的某个子类型
     * @param c
     */
    public void addAll2(DynamicArray<? extends E> c) {
        for(int i=0; i<c.size; i++) {
            add(c.get(i));
        }
    }

    /**
     * 无限定通配符(只能读不能写)
     * 也可以写成： public static <T> int indexOf(DynamicArray<T> arr, Object elm) {}
     * @param arr
     * @param elm
     * @return
     */
    public static int indexOf(DynamicArray<?> arr, Object elm) {
        for(int i=0; i<arr.size; i++) {
            if(arr.get(i).equals(elm)) {
                return i;
            }
        }
        return -1;
    }

    /**
     * public static <D, S> void copy(DynamicArray<D> dest, DynamicArray<S> src)
     * @param dest
     * @param src
     * @param <D>
     */
    public static <D> void copy(DynamicArray<D> dest, DynamicArray<? extends D> src) {
        for(int i=0; i<src.size; i++) {
            dest.add(src.get(i));
        }
    }


    public static void main(String[] args) {
//        demo01();
        DynamicArray<Number> numbers = new DynamicArray<>();
        DynamicArray<Integer> ints = new DynamicArray<>();
        ints.add(100);
        ints.add(34);
        // 报错
        numbers.addAll(ints);
    }

    private static void demo01() {
        DynamicArray<Double> arr = new DynamicArray<>();
        Random random = new Random();
        int size = 1 + random.nextInt(100);
        for(int i=0; i<size; i++) {
            arr.add(Math.random());
        }
        Double d = arr.get(random.nextInt(size));
        System.out.println(d);
    }
}
